import random
import re
import datetime
import logging
import time

from Common import Common
from utils.DBUtil import DBUtil
from bilibili_api.comment import OrderType
from openprompt.data_utils import InputExample
from model.bert_sarcasm_model.bert_sarcasm_model import BertTextClassifier
from models import MonitoringTask, MonitoredVideo, Comment, MonitoredVideoHistory
from bilibili_api import comment, sync
from bilibili_api import video
from utils.CredentialUtil import CredentialConfig
from utils.ColdUtil import ColdUtil


# 检查数据库是否已存在任务
def check_task_not_in(key):
    session = DBUtil.get_thread_session()
    monitor_task = session.query(MonitoringTask).filter_by(task_name=key).first()
    if monitor_task is None:
        return True
    return False


def check_video_can_in(bvid):
    # if not judge_is_allowed_comment(bvid):
    #     return False
    session = DBUtil.get_thread_session()
    monitor_video = session.query(MonitoredVideo).filter_by(bvid=bvid).first()
    if monitor_video is None:
        if session.query(MonitoredVideoHistory).filter_by(bvid=bvid).first() is None:
            return True
    return False


# 通过re过滤除中英文及数字以外的其他字符
def filter_string(des_string, re_string=''):
    res = re.compile("[^\\u4e00-\\u9fa5^a-z^A-Z^0-9]")
    return res.sub(re_string, des_string)


# 创建监控视频对象并存储
async def save_video(bvid):
    session = DBUtil.get_thread_session()
    error_message = None
    print(f'bvid是{bvid}')
    try:
        v = video.Video(bvid=bvid, credential=CredentialConfig.get_credential())
        # 获取视频信息
        info = await v.get_info()
        bvid = info['bvid']  # 视频bv号
        aid = info['aid']  # 视频av号
        video_name = info['title']  # 视频标题
        tid = info['tid']  # 类别id
        tname = info['tname']  # 类别
        comment_count = info['stat']['reply']  # 评论数量
        pubdate = datetime.datetime.utcfromtimestamp(info['pubdate'])  # 视频发布时间
        views = info['stat']['view']  # 视频浏览量
        danmaku = info['stat']['danmaku']  # 弹幕条数
        favorite = info['stat']['favorite']  # 收藏数量
        coin = info['stat']['coin']  # 投币数
        share = info['stat']['share']  # 分享数
        like = info['stat']['like']  # 点赞数
        dislike = info['stat']['dislike']  # 点踩数量
        duration = info['duration']  # 视频时长
        now_time = datetime.datetime.now()  # 当前条数据记录的时间
        monitoredVideo = MonitoredVideo(bvid=bvid, aid=aid, video_name=video_name, tid=tid, tname=tname,
                                        comment_count=comment_count,
                                        pubdate=pubdate, views=views, danmaku=danmaku, favorite=favorite, coin=coin,
                                        share=share, like=like, dislike=dislike, duration=duration, now_time=now_time,
                                        )
        # print("正在保存" + monitoredVideo.bvid)
        session.add(monitoredVideo)
        session.commit()
    except Exception as e:
        print(f'{bvid}保存失败{str(e)}')
        error_message = str(e)
    return error_message


# 重新爬取视频下所有评论
async def re_crawling_video_comments(bvid):
    try:
        monitor_video = MonitoredVideo.query.filter_by(bvid=bvid).first()
        if not monitor_video:
            return False, "未找到视频"

        # 调用删除评论的函数
        success, result = delete_video_comments(bvid)
        if not success:
            return False, "删除评论时发生错误：" + result

        # 重新爬取评论
        await get_all_comment(bvid)
    except Exception as e:
        return False, "重新爬取评论时发生错误：" + str(e)

    return True, "成功重新爬取并更新评论"


async def get_comments_with_retry(bvid, page):
    max_retries = 10  # 最大重试次数
    retries = 0

    while retries < max_retries:
        try:
            # 尝试获取评论
            c = await comment.get_comments(bvid, comment.CommentResourceType.VIDEO, page, OrderType.TIME,
                                           CredentialConfig.get_credential())
            # 如果成功获取评论，则直接返回评论
            return c
        except Exception as e:
            # 捕获异常并处理
            error_message = str(e)  # 将异常信息转换为字符串并保存
            print(error_message)
            # print(f'{error_message}')
            # logging.error(f'{bvid}报错{error_message}')
            retries += 1
            # logging.error(f'{bvid}重试第{retries}次...')
    # 如果超过最大重试次数仍无法成功，则输出错误信息并返回 None
    print(f'{bvid}无法连接服务器，超过最大重试次数')
    return None


async def get_all_comment(bvid):
    session = DBUtil.get_thread_session()
    error_message = None
    try:
        # 页码
        page = 1
        # 当前已获取数量
        count = 0
        last_comment_time = 0
        # violence_count = 0
        # if Common.JUDGE_SARCASM:
        #     sarcasm_count = 0
        #     did_violence_count = 0
        while True:
            time.sleep(random.randint(3, 6))
            c = await get_comments_with_retry(bvid, page)
            # 存储评论
            if len(c.get('replies')) == 0:
                print(f'{bvid}没有内容了')
                break
            for reply in c.get('replies'):
                content = reply['content']['message']
                # is_violence = 0
                # if Common.JUDGE_SARCASM:
                #     is_sarcasm = 0
                #     is_did_violence = 0
                # try:
                #     is_violence = use_cold_bert(text=content)
                # except Exception as e:
                #     print(f'{bvid}报错{str(e)}')
                # try:
                #     is_sarcasm = use_sarcasm_bert(topic=monitor_name, text=content)
                # except Exception as e:
                #     logging.error(f'{bvid}报错{str(e)}')
                # if is_violence == 1:
                #     violence_count += 1
                # if is_sarcasm == 1:
                #     sarcasm_count += 1
                # if is_violence == 1 or is_sarcasm == 1:
                #     is_did_violence = 1
                #     did_violence_count += 1
                account_id = reply.get('mid')
                rpid = reply.get('rpid')
                oid = reply.get('oid')
                type = reply.get('type')
                publish_time = datetime.datetime.fromtimestamp(reply.get('ctime'))
                account_name = reply['member']['uname']
                account_sex = reply['member']['sex']
                account_level = reply['member']['level_info']['current_level']
                if last_comment_time == 0:
                    last_comment_time = publish_time
                # comment_object = Comment(monitored_video_bvid=bvid, content=content, is_violence=is_violence,
                #                          is_sarcasm=is_sarcasm, is_did_violence=is_did_violence, account_id=account_id,
                #                          account_sex=account_sex,
                #                          account_level=account_level,
                #                          account_name=account_name, publish_time=publish_time, rpid=rpid, oid=oid,
                #                          type=type)
                comment_object = Comment(bvid=bvid, content=content,
                                         account_id=account_id,
                                         account_sex=account_sex,
                                         account_level=account_level,
                                         account_name=account_name, publish_time=publish_time, rpid=rpid, oid=oid,
                                         type=type)
                session.add(comment_object)
                count += 1
            # print(f'{bvid}爬取到第{page}页')
            # 增加页码
            page += 1
        # 修改视频评论总数
        video_obj = session.query(MonitoredVideo).filter_by(bvid=bvid).first()
        video_obj.comment_count = count
        # video_obj.violence_count = violence_count
        # video_obj.sarcasm_count = sarcasm_count
        # video_obj.did_violence_count = did_violence_count
        video_obj.last_comment_time = last_comment_time
        # 修改提交到数据库
        session.commit()
        # 检测是否讽刺
        juage_violence(bvid=bvid)
        print(f'{bvid}提交数据库')
    except Exception as e:
        session.rollback()
        # 捕获异常并处理
        error_message = str(e)  # 将异常信息转换为字符串并保存
        print(f'{bvid}报错{error_message}')
        return error_message
        # 爬取监控视频的评论区
    return error_message


# 删除视频下所有评论
def delete_video_comments(bvid):
    session = DBUtil.get_thread_session()
    try:
        # 使用更高效的方式批量删除评论
        deleted_count = session.query(Comment).filter_by(bvid=bvid).delete()
        session.commit()
        return True, deleted_count  # 返回成功标志和删除的评论数量
    except Exception as e:
        session.rollback()  # 如果删除操作失败，则回滚
        return False, str(e)  # 返回失败标志和错误信息


# 增量爬取视频下的评论
async def update_comment(bvid):
    session = DBUtil.get_thread_session()
    error_message = None
    try:
        # 获取到这条视频的最近评论时间
        video_obj = session.query(MonitoredVideo).filter_by(bvid=bvid).first()
        last_comment_time = video_obj.last_comment_time

        # 得到最新评论(不一定是一条因为可能存在并发)，后面用于比对
        last_comments = session.query(Comment).filter_by(bvid=bvid,
                                                         publish_time=last_comment_time).all()
        if len(last_comments) == 0:
            return await get_all_comment(bvid)

        new_last_comment_time = last_comment_time
        # 页码
        page = 1
        # 新增的评论数
        new_count = 0
        # violence_count = 0
        # sarcasm_count = 0
        # did_violence_count = 0
        while True:
            time.sleep(random.randint(3, 6))
            c = await get_comments_with_retry(bvid, page)
            if len(c.get('replies')) == 0:
                break
            for reply in c.get('replies'):
                publish_time = datetime.datetime.fromtimestamp(reply.get('ctime'))
                content = reply['content']['message']
                # is_violence = 0
                # is_sarcasm = 0
                # is_did_violence = 0
                # try:
                #     is_violence = use_cold_bert(text=content)
                # except Exception as e:
                #     logging.error(f'{bvid}报错{str(e)}')
                # try:
                #     is_sarcasm = use_sarcasm_bert(topic=monitor_name, text=content)
                # except Exception as e:
                #     logging.error(f'{bvid}报错{str(e)}')
                # if is_violence == 1 or is_sarcasm == 1:
                #     is_did_violence = 1
                account_id = reply.get('mid')
                rpid = reply.get('rpid')
                oid = reply.get('oid')
                type = reply.get('type')
                account_name = reply['member']['uname']
                account_sex = reply['member']['sex']
                account_level = reply['member']['level_info']['current_level']
                # 判定是否结束，当前评论时间早于数据库内最晚时间，则停止
                # 如果先出现小于了，说明等于的那条评论，也就是数据库最新的评论被删除了，所以直接不用存了
                if publish_time < last_comment_time:
                    if new_count != 0:
                        video_obj.comment_count += new_count
                        video_obj.last_comment_time = new_last_comment_time
                        # video_obj.violence_count = video_obj.violence_count + violence_count
                        # video_obj.sarcasm_count = video_obj.sarcasm_count + sarcasm_count
                        # video_obj.did_violence_count = video_obj.did_violence_count + did_violence_count
                        # 修改提交到数据库
                        session.commit()
                        # 检测是否讽刺
                        juage_violence(bvid)
                    return error_message
                # 如果出现等于了，需要判断一下是否出现过（并发情况）
                if publish_time == last_comment_time:
                    for i in last_comments:
                        if i.content == content and int(i.account_id) == account_id:
                            if new_count != 0:
                                video_obj.comment_count += new_count
                                video_obj.last_comment_time = new_last_comment_time
                                # video_obj.violence_count = video_obj.violence_count + violence_count
                                # video_obj.sarcasm_count = video_obj.sarcasm_count + sarcasm_count
                                # video_obj.did_violence_count = video_obj.did_violence_count + did_violence_count
                                # 修改提交到数据库
                                session.commit()
                                # 检测是否评论讽刺
                                juage_violence(bvid)
                            return error_message
                # comment_object = Comment(monitored_video_bvid=bvid, content=content, is_violence=is_violence,
                #                          is_sarcasm=is_sarcasm, is_did_violence=is_did_violence, account_id=account_id, account_sex=account_sex,
                #                          account_level=account_level,
                #                          account_name=account_name, publish_time=publish_time, rpid=rpid, oid=oid,
                #                          type=type)
                comment_object = Comment(bvid=bvid, content=content,
                                         account_id=account_id,
                                         account_sex=account_sex,
                                         account_level=account_level,
                                         account_name=account_name, publish_time=publish_time, rpid=rpid, oid=oid,
                                         type=type)
                session.add(comment_object)
                # if is_violence == 1:
                #     violence_count += 1
                # if is_sarcasm == 1:
                #     sarcasm_count += 1
                # if is_did_violence == 1:
                #     did_violence_count += 1
                if new_count == 0:
                    new_last_comment_time = publish_time
                new_count += 1
            # 增加页码
            page += 1
    except Exception as e:
        # 捕获异常并处理
        session.rollback()
        error_message = str(e)  # 将异常信息转换为字符串并保存
        logging.error(f'{bvid}报错{error_message}')
    return error_message


def juage_violence(bvid):
    session = DBUtil.get_thread_session()
    print(str(datetime.datetime.now()) + "开始判断暴力")
    video_b = session.query(MonitoredVideo).filter_by(bvid=bvid).first()
    topic = video_b.video_name
    comments = session.query(Comment).filter_by(bvid=bvid, is_judge=False).all()
    batch_size = 8
    dataset = []
    point = 0
    for i, c in enumerate(comments):
        content = ''
        if len(c.content) > 500:
            content = c.content[:500]
        content = c.content
        c.is_violence = ColdUtil.classify_text(content)
        if c.is_violence == 1:
            video_b.violence_count += 1
        dataset.append(InputExample(guid=i, text_a=topic, text_b=content, label=0))

        if (i + 1) % batch_size == 0 or i == len(comments) - 1:
            sarcasm_result = BertTextClassifier.batch_predict(dataset)
            result_index = 0  # 用于追踪result列表中的索引

            for j in range(point, i + 1):
                # 检查result列表是否已经处理完毕
                if result_index < len(sarcasm_result):
                    # 将result中的预测结果赋值给对应的评论的is_sarcasm属性
                    if sarcasm_result[result_index] == 1:
                        video_b.sarcasm_count += 1
                        comments[j].is_sarcasm = 1
                    if comments[j].is_sarcasm == 1 or comments[j].is_violence == 1:
                        comments[j].is_did_violence = 1
                        video_b.did_violence_count += 1
                    comments[j].is_judge = True
                    result_index += 1
                else:
                    # 如果result列表已经处理完毕，则退出循环
                    break
            # print(result)
            # 处理结果
            dataset = []
            point = i
    session.commit()

    print(str(datetime.datetime.now()) + "判断暴力完成")
